/*
 *
 * Copyright (c) 2023 - future
 * @author fzh
 * @email e_341877120_mail@163.com
 * @link https://mvnrepository.com/search?q=io.github.xrfzh.cn
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package cn.xrfzh.common.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Configuration
public class JacksonConfig implements WebMvcConfigurer {

	/**
	 * 默认日期时间格式
	 */
	public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";

	/**
	 * 默认日期格式
	 */
	public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";

	/**
	 * 默认时间格式
	 */
	public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

	/**
	 * 配置响应体格式
	 *
	 * @param configurer 配置器
	 */
	@Override
	public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
		configurer.defaultContentType(MediaType.APPLICATION_JSON)
				.mediaType("json", MediaType.APPLICATION_JSON)
				.mediaType("x-www-form-urlencoded", MediaType.APPLICATION_FORM_URLENCODED);
	}

	/**
	 * 移除默认的MappingJackson2HttpMessageConverter，添加自定义的MappingJackson2HttpMessageConverter
	 */
	@Override
	public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
		converters.removeIf(messageConverter -> messageConverter instanceof
				MappingJackson2HttpMessageConverter);
		converters.add(mappingJackson2HttpMessageConverter());
	}

	/**
	 * 自定义MappingJackson2HttpMessageConverter
	 */
	@Primary
	@Bean("mappingJackson2HttpMessageConverter")
	@ConditionalOnMissingBean(ObjectMapper.class)
	public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
		MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
		ObjectMapper objectMapper = new ObjectMapper();
		// 设置序列化参数
		setSerializationConfig(objectMapper);
		// 设置日期时间格式和时区
		setDateFormatAndTimeZone(objectMapper);
		// 注册Java8日期类型模块
		JavaTimeModule javaTimeModule = new JavaTimeModule();
		registerJavaTimeModules(javaTimeModule);
		objectMapper.registerModule(javaTimeModule);
		// 注册Simple模块
		SimpleModule simpleModule = new SimpleModule();
		// 封装类型 Long ==> String
		simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
		// 封装类型 long ==> String（不建议，按需配置）
		// simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
		objectMapper.registerModule(simpleModule);
		converter.setObjectMapper(objectMapper);
		return converter;
	}

	/**
	 * 设置序列化参数
	 */
	private void setSerializationConfig(ObjectMapper objectMapper) {
		objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
		objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
		objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
	}

	/**
	 * 设置时区和日期时间格式
	 */
	private void setDateFormatAndTimeZone(ObjectMapper objectMapper) {
		TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai");
		objectMapper.setTimeZone(timeZone);
		SimpleDateFormat dateFormat = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
		dateFormat.setTimeZone(timeZone);
		objectMapper.setDateFormat(dateFormat);
	}

	/**
	 * 注册Java8日期类型的序列化和反序列化方法
	 */
	private void registerJavaTimeModules(JavaTimeModule javaTimeModule) {
		DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT);
		DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);
		DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT);
		javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
		javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
		javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));
		javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));
		javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter));
		javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter));
	}

}
